Ajouter du brouillard sur une map
Écrit le 03/07/2003 par Bicou
Dernière mise à
jour : 30/01/2006
Introduction
Me voilà de retour pour un nouveau tutorial, ce coup-ci c'est pour créer un env_fog sur votre map. Si vous êtes pas convaincus ou que vous savez pas ce que c'est :

Le fog OpenGL dans une map
Le problème c'est que ça ne marche qu'en mode OpenGL :`(
Comprendre le tut
Il existe, dans les bibliothèques de fonctions de l'OpenGL, une fonction qui permet d'afficher un fog dans un moteur 3D. Grâce au nouveau SDK (2.2 et plus), valve nous permet d'utiliser ces fonctions avec le moteur de HL, donc dans votre mod vous pourrez afficher ce fog, et même utiliser toutes les fonctions OpenGL que vous connaissez.
Nous allons d'abord créer une entité qui va nous permettre de mettre un
env_fog dans une map (dans Worldcraft) et de configurer ses paramètres
(couleur, etc.). Ensuite on va créer un message qui ne sera envoyé qu'une fois,
qui va dire au client quelle est la couleur du fog, et les différents
paramètres. Le client va récupérer le message par l'intermédiaire d'une classe
dérivée de ChudBase, pour faire du code propre, et à chaque frame,
dans la fonction Draw() de votre classe CHudBase, vous
allez lancer les fonctions OpenGL qui permettent donc l'affichage du fog.
Côté serveur
Donc là c'est très simple, on crée une nouvelle entité. On crée tout d'abord sa classe, allez dans cbase.h (eh oui, faudra beaucoup compiler :{), et tout à la fin du fichier :
//Bicou class CClientFog : public CBaseEntity { public: void Spawn( void ); void KeyValue( KeyValueData *pkvd ); float m_iStartDist; float m_iEndDist; }; //Bicou -fin
Voilà, on a notre classe, maintenant faut l'implémenter (la définir complètement). Allez dans triggers.cpp, tout à la fin du fichier :
//Bicou void CClientFog :: KeyValue( KeyValueData *pkvd ) { if (FStrEq(pkvd->szKeyName, "startdist")) { m_iStartDist = atoi(pkvd->szValue); pkvd->fHandled = true; } else if (FStrEq(pkvd->szKeyName, "enddist")) { m_iEndDist = atoi(pkvd->szValue); pkvd->fHandled = true; } else { CBaseEntity::KeyValue( pkvd ); } } void CClientFog :: Spawn ( void ) { pev->movetype = MOVETYPE_NOCLIP; pev->solid = SOLID_NOT; // Remove model & collisions pev->renderamt = 0; // The engine won't draw this model if this // is set to 0 and blending is on pev->rendermode = kRenderTransTexture; } LINK_ENTITY_TO_CLASS( env_fog, CClientFog ); //Bicou -fin
Ca y est, l'entité est créée. C'est un env_fog. Maintenant faut dire au client qu'on veut qu'il affiche le fog. On va créer un nouveau message. Direction player.cpp, au début du fichier :
int gmsgTextMsg = 0; int gmsgSetFOV = 0; int gmsgShowMenu = 0; int gmsgGeigerRange = 0; int gmsgTeamNames = 0; int gmsgFog = 0; //Nouveau code void LinkUserMessages( void ) { // Already taken care of? if ( gmsgSelAmmo )
Et un peu plus bas :
gmsgShowMenu = REG_USER_MSG( "ShowMenu", -1 );
gmsgShake = REG_USER_MSG("ScreenShake", sizeof(ScreenShake));
gmsgFade = REG_USER_MSG("ScreenFade", sizeof(ScreenFade));
gmsgAmmoX = REG_USER_MSG("AmmoX", 2);
gmsgTeamNames = REG_USER_MSG( "TeamNames", -1 );
//FOG:
gmsgFog = REG_USER_MSG( "Fog", 7 );
}
LINK_ENTITY_TO_CLASS( player, CBasePlayer );
Bon ça y est on a notre message, mais maintenant faut l'envoyer. Toujours
dans player.cpp, ligne 3700 et des brouettes (dans la
fonction CBasePlayer::UpdateClientData)
if ( !m_fGameHUDInitialized ) { MESSAGE_BEGIN( MSG_ONE, gmsgInitHUD, NULL, pev ); MESSAGE_END(); g_pGameRules->InitHUD( this ); m_fGameHUDInitialized = true; //BICOU -debut CBaseEntity *pEntity = NULL; pEntity = UTIL_FindEntityByClassname( pEntity, "env_fog" ); if ( pEntity ) { ALERT( at_console, "Map has fog!\n" ); CClientFog *pFog = (CClientFog *)pEntity; MESSAGE_BEGIN( MSG_ONE, gmsgFog, NULL, pev ); WRITE_BYTE( pFog->pev->rendercolor.x ); WRITE_BYTE( pFog->pev->rendercolor.y ); WRITE_BYTE( pFog->pev->rendercolor.z ); WRITE_SHORT( pFog->m_iStartDist ); WRITE_SHORT( pFog->m_iEndDist ); MESSAGE_END(); } else { ALERT( at_console, "Map doesn't have any fog!\n" ); } //Bicou -fin if ( g_pGameRules->IsMultiplayer() ) { FireTargets( "game_playerjoin", this, this, USE_TOGGLE, 0 ); } }
Là c'est pas bien compliqué : on cherche s'il y a un env_fog
dans la map, grâce à la fonction UTIL_FindEntityByClassname. S'il y
en a un, on envoie ses caractéristiques, soit pour les trois premiers sa couleur
en RGB (BYTE), et pour les deux suivants, les deux variables membres de la
classe : StartDist et EndDist (SHORT). Je vous
explique à quoi elles servent en fin de tut.
Voilà, maintenant que le message est envoyé, on passe au client.
Côté client
On va faire ça proprement, c'est à dire avec une classe dérivée de
CHudBase. Ceux qui connaissent pas, c'est un bon moyen d'apprendre
car c'est vraiment très utile. Il suffit de créer un nouveau fichier :
fog.cpp, et de mettre ça dedans :
/*** * * Copyright (c) 1996-2001, Valve LLC. All rights reserved. * * this product contains software technology licensed from Id * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. * All Rights Reserved. * * Use, distribution, and modification of this source code and/or resulting * object code is restricted to non-commercial enhancements to products from * Valve LLC. All other use, distribution, or modification is prohibited * without written permission from Valve LLC. * ****/ // // fog.cpp // // implementation of CHudFog class // #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <glgl.h> #include "r_studioint.h" extern engine_studio_api_t IEngineStudio; #include "hud.h" #include "cl_util.h" #include "parsemsg.h" #include <stdio.h> #include <string.h> DECLARE_MESSAGE(m_Fog, Fog) int CHudFog::Init(void) { HOOK_MESSAGE(Fog); gHUD.AddHudElem(this); return 1; }; int CHudFog::VidInit(void) { return 1; }; int CHudFog:: MsgFunc_Fog(const char *pszName, int iSize, void *pbuf ) { m_iFlags |= HUD_ACTIVE; BEGIN_READ( pbuf, iSize ); r = READ_SHORT(); g = READ_SHORT(); b = READ_SHORT(); s = READ_SHORT(); e = READ_SHORT(); return 1; } int CHudFog::Draw(float flTime) { if( r == 0 && g == 0 && b == 0 )//si c'est noir on affiche pas car on ne verrait rien return 1; if ( IEngineStudio.IsHardware() != 1) return 1; int a = 128; float fDensity = 0.5, fStart = s, fEnd = e; float fogColor[4]; fogColor[0] = ((GLfloat)r)/255.0f; fogColor[1] = ((GLfloat)g)/255.0f; fogColor[2] = ((GLfloat)b)/255.0f; fogColor[3] = ((GLfloat)a)/255.0f; glFogi(GL_FOG_MODE, GL_LINEAR); glFogfv(GL_FOG_COLOR, fogColor); glFogf(GL_FOG_DENSITY, fDensity); glHint(GL_FOG_HINT, GL_NICEST); glFogf( GL_FOG_START, fStart); glFogf( GL_FOG_END, fEnd ); glEnable(GL_FOG); return 1; }
Ach! Ça fait un bout de code ! Mais bon, c'est que du copier-coller. Essayez de comprendre le code, c'est le but d'un tut je le rappelle...
Alors oui, on parle de CHudFog, mais où l'a-t-on déclarée ?
Bonne question. Alors c'est dans hud.h :
//Bicou //----------------------------------------------------- // class CHudFog : public CHudBase { public: int Init( void ); int VidInit( void ); int Draw(float flTime); int MsgFunc_Fog(const char *pszName, int iSize, void *pbuf ); private: int r, g, b, s, e; }; //Bicou -fin
Les membres, ce sont les membres habituels des CHudBase, r
g b c'est la couleur, s et e ce sont les deux
variables du fog : StartDist et EndDist.
Mais il manque un truc : faut déclarer une instance de notre classe. C'est dans hud.h, ligne 650 et quelques :
CHudGeiger m_Geiger;
CHudBattery m_Battery;
//Bicou
CHudFog m_Fog;
CHudTrain m_Train;
CHudFlashlight m_Flash;
Voilà. Dernier petit truc : les initialisations, c'est dans hud.cpp :
m_Train.Init();
m_Battery.Init();
m_Fog.Init();//Bicou
m_Flash.Init();
m_Message.Init();
et puis, plus bas :
m_Train.VidInit(); m_Battery.VidInit(); m_Fog.VidInit(); m_Flash.VidInit(); m_Message.VidInit();
Enfin ! C'est fini, votre fog est op. Maintenant on va passer aux réglages.
Mais nous avons oublié de définir les fonctions OpenGL que nous utilisons. Elles sont déclarées dans windows.h et gl/gl.h (Allez sur le site d'OpenGL si vous n'avez pas les lib OpenGL), mais il faut leur implémentation qui est dans opengl32.lib. Allez dans et pour le project client, allez dans l'onglet et dans , mettez opengl32.lib à la fin de la liste. Compilez et c'est nickel !
Côté FGD
Le FGD ??? Qu'est-ce que c'est que cette connerie ??? Un tut sur ce sujet est disponible sur ce site.
Beh oui, faut ajouter notre entité dans le FGD. Ouvrez-le (notepad), et cherchez env_fade. Juste après, on met notre entité :
@PointClass base(Targetname) = env_fog : "Brouillard"
[
startdist(string) : "startdist" : 50
enddist(string) : "enddist" : 500
rendercolor(color255) : "Color (R G B)" : "0 0 0"
]
Voilà, c'est fait. D'ailleurs je me permets de rajouter un petit élément au
tut sur le FGD : un type de variable. Vous voyez que j'ai utilisé le type
color255... Eh bien vous allez être contents : ça définit la
couleur du fog, et pas besoin de se faire chier avec notre KeyValue, ça se met
AUTOMATIQUEMENT dans pev->rendercolor. Elle est pas belle la vie !!!
Les variables, j'ai rien compris
D'abord, si vous avez rien compris : lisez le tut sur le FGD, ça devrait vous aider.
Sinon, les variables :
StartDist: C'est la distance entre vous et le brouillard. Je vous conseille 50 (unités).EndDist: C'est l'épaisseur du nuage. Je vous conseille 500 (faites comme vous voulez).rendercolor: la couleur du brouillard. N'hésitez pas à mettre des couleurs sombres (en dessous de 100)- Les valeurs de
StartDistetEndDistrestent un mystère : certains mettent 0.5 et 2, moi je mets 50 et 500, ça donne un effet similaire ... Donc à mon avis,EndDistest relatif àStartDist, c'est-à-dire que si on a SD = 0.5 et ED = 5, ou SD = 20 et ED = 200, on a EXACTEMENT le même résultat. Je n'en suis pas sûr, mais c'est la chose la plus probable.
Ceux qui ont un effet pourri, moi j'utilise : StartDist à
50, EndDist à 500, rendercolor à 60 60 60, et ça rend
très bien (j'ai une GeForce DDR, je sais pas si ça change quelque
chose...).



